#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <fstream> //C++的文件操作
#include <sstream>
#include <time.h>
namespace LogModel
{

    // 二、刷新策略:
    class LogStrategy
    { // 刷新策略基类
    public:
        ~LogStrategy() = default;                         // 不需要析构函数
        virtual void RefleshLog(std::string message) = 0; // ​纯虚函数​,它通过在声明时使用=0语法标记，
        // 强制要求派生类必须实现该函数,包含纯虚函数的类称为抽象基类,不能直接实例化。
    };

    class ConsoleLogStrategy : public LogStrategy // 1.显示器策略
    {
    public:
        ConsoleLogStrategy()
        {
            pthread_mutex_init(&mutex, nullptr);
        }

        virtual void RefleshLog(std::string message) override // override用于显式标记派生类中
                                                              // 重写(override)基类虚函数的成员函数。
        {
            pthread_mutex_lock(&mutex);     // 显示器也是共享资源，需要加锁
                                            // 否则打印的消息是凌乱的
            std::cout << message << "\r\n"; //\r\n : 回车+换行
            pthread_mutex_unlock(&mutex);
        }
        ~ConsoleLogStrategy()
        {
            pthread_mutex_destroy(&mutex);
        }

    private:
        pthread_mutex_t mutex;
    };

    const std::string defaultpath = "./";     // 默认在当前目录下创建my.log日志文件
    const std::string defaultname = "my.log"; //.log其实就是文本文件

    class FileLogStrategy : public LogStrategy // 文件策略,往指定文件写入日志
    {
    public:
        FileLogStrategy(const std::string &path = defaultpath, const std::string &name = defaultname)
            : _path(path),
              _name(name)
        {
            pthread_mutex_init(&mutex, nullptr);
        }
        virtual void RefleshLog(std::string message) override // override用于显式标记派生类中
                                                              // 重写(override)基类虚函数的成员函数。
        {
            pthread_mutex_lock(&mutex); // 目标文件也是共享资源，需要加锁,否则输出的消息是凌乱的

            std::string filename = _path + _name;
            // C++的文件操作：
            // 1.创建输出流对象：
            std::ofstream out(filename, std::ios::app); // 以追加的方式打开目标文件
                                                        //,如果文件不存在会自动创建

            // 2.将信息输出到目标文件:  //out对象 重载了<<
            out << message << "\r\n";

            pthread_mutex_unlock(&mutex);
        }
        ~FileLogStrategy()
        {
            pthread_mutex_destroy(&mutex);
        }

    private:
        std::string _path; // 目标文件路径
        std::string _name; // 目标文件名
        pthread_mutex_t mutex;
    };

    // 一、形成完整的日志消息：时间、日志等级等 + 用户的输入信息
    //(1)日志等级
    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    std::string Level_To_String(LogLevel &loglevel)
    {
        switch (loglevel)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
            break;
        case LogLevel::INFO:
            return "INFO";
            break;
        case LogLevel::WARNING:
            return "WARINING";
            break;
        case LogLevel::ERROR:
            return "ERROR";
            break;
        case LogLevel::FATAL:
            return "FATAL";
            break;
        }
        return "";
    }
    //(2)获取当前时间
    std::string GetTime()
    {
        time_t curr = time(nullptr); // 获取当前时间戳
        struct tm cur_time;          // 将转换后的时间放在这个结构体里

        localtime_r(&curr, &cur_time); // 将时间戳转化为时间

        char timebuffer[128];
        snprintf(timebuffer, sizeof(timebuffer) - 1, "%d-%02d-%02d %02d-%02d-%02d",
                 cur_time.tm_year + 1900,
                 cur_time.tm_mon + 1,
                 cur_time.tm_mday,
                 cur_time.tm_hour,
                 cur_time.tm_min,
                 cur_time.tm_sec);
        return timebuffer;
    }

    // 该类用于： 1.形成完整的日志  2.根据不同的刷新策略来刷新
    class Logger
    {
    public:
        Logger()
        {
            SetConsoleLogStrategy(); // 默认刷新策略设置为向显示器刷新
        }
        void SetConsoleLogStrategy()
        { // 刷新策略设置为向显示器刷新
            _refresh = std::make_unique<ConsoleLogStrategy>();
        }
        void SetFileLogStrategy()
        { // 刷新策略设置为向指定文件刷新
            _refresh = std::make_unique<FileLogStrategy>();
        }

        class LogMessage // 将日志消息封装成一个Logger的内部类
        {
        public:
            LogMessage(const std::string &src_name, const LogLevel &loglevel, int line, Logger &logger)
                : _src_name(src_name),
                  _time(GetTime()),
                  _loglevel(loglevel),
                  _line(line),
                  _pid(getpid()),
                  _logger(logger)
            {
                // 将日志的左半部分合并起来：
                // stringstream 用法：用于在字符串和其他数据类型之间进行转换，或者对字符串进行流式操作
                std::stringstream ss;
                ss << "[" << _time << "] "
                   << "[" << Level_To_String(_loglevel) << "] "
                   << "[" << _pid << "] "
                   << "[" << _src_name << "] "
                   << "[" << _line << "]"
                   << " - ";
                _logmessage = ss.str();
            }
            // 日志的右半部分:
            template <class T>
            LogMessage &operator<<(const T &info)
            {
                std::stringstream ss;
                ss << info;
                _logmessage += ss.str();
                return *this; // 这里的*this 是LogMessage对象
                              // 返回它是因为可能是用户这样使用：Logger()<<....<<....<<... 这样的连续流
            }

            ~LogMessage() // 内部类可以访问外部类的私有成员,但是要通过外部类调用
            {
                // 当内部类LogMessage销毁时，就刷新数据
                _logger._refresh->RefleshLog(_logmessage);
            }

        private:
            std::string _time;     // 时间
            LogLevel _loglevel;    // 日志等级
            pid_t _pid;            // pid
            std::string _src_name; // 源文件名
            int _line;             // 行号

            // 上面信息合并起来的一条完整的日志消息：
            std::string _logmessage;

            Logger &_logger; // 外部类
        };

        LogMessage operator()(const LogLevel &loglevel, const std::string &src_name, int line)
        {
            // 构建一个临时对象: 当对象销毁时，就在析构时刷新
            return LogMessage(src_name, loglevel, line, *this); // 这里的*this是Logger对象
        }
        ~Logger()
        {
        }

    private:
        std::unique_ptr<LogStrategy> _refresh; // 刷新策略.这里只是定义了一个指针对象
    };

    Logger logger;
#define LOG(level) logger(level, __FILE__, __LINE__) // 从此用户只需要传LogLevel的类型即可
#define SET_CONSOLE_LOG_STRATEGY() log.SetConsoleLogStrategy()
#define SET_FILE_LOG_STRATEGY() log.SetFileLogStrategy()

}